// Thud - a dynamic object-oriented language
// Copyright (C) 2009,2010 David H. Hovemeyer <david.hovemeyer@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Abstract Syntax Tree Node class

#ifndef FE_ASTNODE_H
#define FE_ASTNODE_H

#include <cstdint>
#include <deque>
#include <string>
#include "fe/ASTNodeFlag.h"
#include "fe/Import.h"
#include "fe/Location.h"

class IASTNodeVisitor;
class Import;
class Token2;
class SymbolTable;
class SymbolTableEntry;

/**
 * A node in the abstract syntax tree of a Thud source file.
 *
 * An ASTNode may have a string value, integer value, or
 * Import.  For example, an AST node representing a variable reference
 * will have the name of the variable set as a string value.
 */
class ASTNode {
public:
	/** Types of ASTNodes. */
	enum ASTNodeType {
		/** Overall translation unit. */
		AST_UNIT,
		/** List of imports. */
		AST_IMPORT_LIST,
		/** An import. */
		AST_IMPORT,
		/** A static import (of a method from a module). */
		AST_STATIC_IMPORT,
		/** A list of statements. */
		AST_STATEMENT_LIST,
		/** A statement. */
		AST_STATEMENT,
		/** A while statement. */
		AST_WHILE_STATEMENT,
		/** An if statement. */
		AST_IF_STATEMENT,
		/** A return statement. */
		AST_RETURN_STATEMENT,
		/** An expression involving a binary operator. */
		AST_BINARY_OP_EXPR,
		/** An expression involving a unary operator. */
		AST_UNARY_OP_EXPRESSION,
		/** A field reference expression. */
		AST_FIELD_REF_EXPR,
		/** A method call expression. */
		AST_METHOD_CALL_EXPR,
		/** A call to a module method imported using a static import. */
		AST_MODULE_METHOD_CALL_EXPR,
		/** A variable reference expression. */
		AST_VAR_REF_EXPR,
		/** An integer literal expression. */
		AST_INT_LITERAL_EXPR,
		/** A boolean literal expression. */
		AST_BOOLEAN_LITERAL_EXPR,
		/** A symbol literal expression. */
		AST_SYMBOL_LITERAL_EXPR,
		/** A string literal expression. */
		AST_STRING_LITERAL_EXPR,
		/** A self reference expression (i.e., reference to the receiver object). */
		AST_SELF_REF_EXPR,
		/** A new expression (creation of object). */
		AST_NEW_EXPR,
		/** A nil expression (reference to the special "nil" object). */
		AST_NIL_EXPR,
		/** A class. */
		AST_CLASS,
		/** A module (singleton class). */
		AST_MODULE,
		/** List of class members. */
		AST_MEMBER_LIST,
		/** A method definition. */
		AST_METHOD,
		/** A magic (native) method declaration. */
		AST_MAGIC_METHOD,
		/** A field declaration. */
		AST_FIELD,
		/** A list of variable declarations. */
		AST_VAR_LIST,
		/** A single variable declaration. */
		AST_VAR,
		/** List of method call argument expressions. */
		AST_ARG_LIST,
	};

	static std::wstring getAstNodeTypeName(ASTNodeType nodeType);

	typedef std::deque<ASTNode *> ChildList;

private:
	ASTNodeType m_type;
	Location m_loc;
	ChildList m_childList;
	int m_flags;
	std::wstring m_sval;
	union {
		int32_t m_ival;
		Import *m_import;
		SymbolTable *m_symtab;
		SymbolTableEntry *m_entry;
	};

	ASTNode(const ASTNode &other);
	ASTNode& operator=(const ASTNode &rhs);


public:
	/**
	 * Constructor.
	 *
	 * @param type the type
	 * @param loc  the source location
	 */
	ASTNode(ASTNodeType type, const Location &loc);

	/**
	 * Destructor.
	 */
	virtual ~ASTNode();

	/** Get the source location. */
	const Location &getLocation() const;

	/**
	 * Set the source location.
	 * @param loc the source location
	 */
	void setLocation(const Location &loc);

	/**
	 * Set an AST node flag.
	 *
	 * @param flag the flag to set
	 */
	void setFlag(ASTNodeFlag flag);

	/**
	 * Determine if an AST node flag is set.
	 *
	 * @param flag the flag to check
	 */
	bool isFlagSet(ASTNodeFlag flag) const;
	
	/**
	 * Set a string value.
	 *
	 * @param sval the string value to set
	 */
	void setStringValue(const std::wstring &sval);

	/**
	 * Set a string value from the lexeme of the given token.
	 *
	 * @param t the token whose lexeme will be set as this node's string value
	 */
	void setStringValueFromToken(Token2 *t);

	/**
	 * Set a string value from the string literal whose lexeme is
	 * stored in the given token.
	 *
	 * @param t a string literal token whose lexeme should be decoded and set as this node's string value
	 * @return true if token contains a legal string literal, false if it does not
	 */
	bool setStringValueFromStringLiteral(Token2 *t);

	/**
	 * Get the string value of this node.
	 */
	const std::wstring &getStringValue() const;

	/**
	 * Set an integer value.
	 *
	 * @param ival the integer value to set
	 */
	void setIntValue(int32_t ival);

	/**
	 * Get the integer value of this node.
	 */
	int32_t getIntValue() const;

	/**
	 * Adopt an Import object.
	 *
	 * @param import the Import object to adopt
	 */
	void adoptImport(Import *import);

	/**
	 * Get the Import object adopted by this node.
	 */
	const Import *getImport() const;

	/**
	 * Adopt a SymbolTable object.
	 */
	void adoptSymbolTable(SymbolTable *symtabToAdopt);

	/**
	 * Get this node's SymbolTable object.
	 */
	SymbolTable *getSymbolTable();

	/**
	 * Adopt a SymbolTableEntry object.
	 */
	void adoptSymbolTableEntry(SymbolTableEntry *entryToAdopt);

	/**
	 * Get this node's SymbolTableEntry object.
	 */
	SymbolTableEntry *getSymbolTableEntry();

	/**
	 * Get the AST node type of this node.
	 */
	ASTNodeType getType() const;

	/**
	 * Prepend given child node to the beginning of this node's list of children.
	 *
	 * @param child node to prepend
	 */
	void prependChild(ASTNode *child);

	/**
	 * Append given child node to the end of this node's list of children.
	 *
	 * @param child node to append
	 */
	void appendChild(ASTNode *child);

	/**
	 * Get this node's list of children.
	 */
	ChildList& getChildList();

	/**
	 * Return true if this node represents an executable statement.
	 */
	bool isExecutableStatement() const;

	/**
	 * Return true if this ASTNode is a class or a module.
	 */
	bool isClassOrModule() const;

	/**
	 * Accept given AST node visitor.
	 *
	 * @param visitor the visitor to accept
	 */
	void accept(IASTNodeVisitor *visitor);

	/**
	 * Recursively destroy the given AST node and its descendents.
	 *
	 * @param ast root of AST to destroy
	 */
	static void destroy(ASTNode *ast);
};

#endif /* FE_ASTNODE_H */
